package gotlib

import (
	"context"
	"database/sql"
	"encoding/json"
	"errors"
	"fmt"
	"math"
	"strconv"
	"strings"

	_ "github.com/go-sql-driver/mysql"
	"github.com/gogf/gf/v2/frame/g"
	"github.com/gogf/gf/v2/util/gconv"
)

const (
	DatabaseDefault = "database.default"
)

// var DB *sql.DB
var ctxBG context.Context

func SplitDbConfig(dblink string) (driverName, dataSourceName string) {
	sA := strings.SplitN(dblink, ":", 2)
	if len(sA) < 2 {
		return "mysql", dblink
	}
	driverName = sA[0]
	dataSourceName = sA[1]
	return
}
func initDB() bool { //连接到MySQL

	var err error
	//DB, err = gotgormv2.GormDbMysql.DB()

	//验证连接
	if err = g.DB().PingMaster(); err != nil {
		g.Log().Error(ctxBG, "初始化数据库错误:", err)
		return false
	}

	return true
}

// TDBExec 通用执行SQL语句函数（增删改，不包含查）
func TDBExec(ctx context.Context, SQL string) (id int64, err error) {
	g.Log().Debug(ctx, "Run func TDBExec(", SQL, ")")
	result, err := g.DB().Exec(ctx, SQL)
	if err != nil {
		g.Log().Error(ctx, "执行SQL语句错误：", SQL, err)
		return -1, errors.New("执行SQL语句错误：" + SQL)
	}
	return result.LastInsertId()
}

// QueryMap 通用查询函数-只有map返回值，没有err返回值
func QueryMap(ctx context.Context, sqlString string, args ...interface{}) []map[string]string {
	g.Log().Debug(ctx, "QueryMap通用查询功能：", sqlString)
	if sqlString == "" {
		g.Log().Info(ctx, "QueryMap 参数为空")
		return nil
	}
	if result, err := g.DB().Query(ctx, sqlString, args...); err != nil {
		g.Log().Error(ctx, "QueryMap DB查询错误：", sqlString, "\n", err)
		return nil
	} else {
		resultMap := make([]map[string]string, len(result))
		for _, row := range result {
			ret := make(map[string]string)
			for k, v := range row {
				ret[k] = gconv.String(v)
			}
			resultMap = append(resultMap, ret)
		}
		return resultMap
	}
	//if rows, err := g.DB().Query(ctx, sqlString); err != nil {
	//	defer rows.Close()
	//	g.Log().Error(ctxBG, "QueryMap DB查询错误：", sqlString, "\n", err)
	//	return nil
	//} else {
	//	defer rows.Close()
	//	ret, err := RowsToString(rows)
	//	if err != nil {
	//		g.Log().Error(ctxBG, "QueryMap DB查询结果转换错误：", sqlString, "\n", err)
	//		return nil
	//	}
	//	return ret
	//}
}

func QueryMapNoCTX(sqlString string, args ...interface{}) []map[string]string {
	ctx := context.Background()
	g.Log().Debug(ctx, "QueryMap通用查询功能：", sqlString)
	if sqlString == "" {
		g.Log().Info(ctx, "QueryMap 参数为空")
		return nil
	}
	if result, err := g.DB().Query(ctx, sqlString, args...); err != nil {
		g.Log().Error(ctx, "QueryMap DB查询错误：", sqlString, "\n", err)
		return nil
	} else {
		resultMap := make([]map[string]string, 0)
		for _, row := range result {
			ret := make(map[string]string)
			for k, v := range row {
				ret[k] = gconv.String(v)
			}
			resultMap = append(resultMap, ret)
		}
		return resultMap
	}
}

// TDBQuery 通用查询函数 原函数func TDBQuery(SQL string) ([]map[string]string, error)
//func TDBQuery(SQL string) ([]map[string]string, error) {
//	g.Log().Debug(ctxBG, "Run func TDBQuery(", SQL, ")")
//
//	rows, err1 := g.DB().Query(SQL) //执行SQL语句，比如select * from users
//	if err1 != nil {
//		g.Log().Error(ctxBG, "执行SQL语句错误：", SQL, err1)
//		return nil, errors.New("执行SQL语句错误：" + SQL)
//	}
//
//	columns, _ := rows.Columns()            //获取列的信息
//	count := len(columns)                   //列的数量
//	var values = make([]interface{}, count) //创建一个与列的数量相当的空接口
//	for i, _ := range values {
//		var ii interface{} //为空接口分配内存
//		values[i] = &ii    //取得这些内存的指针，因后继的Scan函数只接受指针
//	}
//
//	ret := make([]map[string]string, 0) //创建返回值：不定长的map类型切片
//	for rows.Next() {
//		err := rows.Scan(values...) //开始读行，Scan函数只接受指针变量
//
//		if err != nil {
//			g.Log().Error(ctxBG, "执行SQL语句错误：", SQL, err)
//			return nil, errors.New("执行SQL语句错误：" + SQL)
//		}
//		m := make(map[string]string) //用于存放1列的 [键/值] 对
//		for i, colName := range columns {
//			var rawValue = *(values[i].(*interface{})) //读出raw数据，类型为byte
//			b, _ := rawValue.([]byte)
//			v := string(b) //将raw数据转换成字符串
//			m[colName] = v //colName是键，v是值
//		}
//		ret = append(ret, m)
//	}
//	defer rows.Close()
//	if len(ret) != 0 {
//		return ret, nil
//	}
//	return nil, nil
//}

// FormatSQLJson 根据Json字符串里的内容，对SQL语句中的变量进行替换。todo 需要测试
func FormatSQLJson(sSQL string, jsonParams string) string {
	if jsonParams == "" {
		return sSQL
	}
	sFlag := "#^#"
	if strings.Index(sSQL, sFlag) < 0 {
		return sSQL
	}
	g.Log().Debug(ctxBG, sSQL, jsonParams)

	m := make(map[string]interface{})
	if err := json.Unmarshal([]byte(jsonParams), &m); err != nil {
		return sSQL
	}

	for k, v := range m {
		p := strings.Join([]string{sFlag, k, sFlag}, "")
		switch data := v.(type) {
		case string:
			sSQL = strings.ReplaceAll(sSQL, p, strings.Join([]string{data}, ""))
		case bool:
			sSQL = strings.ReplaceAll(sSQL, p, strings.Join([]string{strconv.FormatBool(data)}, ""))
		case float64:
			if data == math.Trunc(data) {
				sSQL = strings.ReplaceAll(sSQL, p, strings.Join([]string{fmt.Sprintf("%.0f", data)}, ""))
			} else {
				strTemp := strings.Trim(strings.Join([]string{fmt.Sprintf("%f", data)}, ""), "0")
				sSQL = strings.ReplaceAll(sSQL, p, strTemp)
			}
		case []string:
			strTemp := strings.Join([]string{strings.Join(data, ",")}, "")
			sSQL = strings.ReplaceAll(sSQL, p, strTemp) //todo
		case []interface{}: //todo
			strTemp := fmt.Sprint(data)
			sSQL = strings.ReplaceAll(sSQL, p, strTemp)
		}
	}
	g.Log().Debug(ctxBG, "Final SQL:", sSQL)
	return sSQL
}

// FormatSQLMap todo 根据Json字符串里的内容，对SQL语句中的变量进行替换。
func FormatSQLMap(sSQL string, paramsMap map[string]string) string {
	//g.Log().Debug(ctxBG, "\n", sSQL, paramsMap)
	if len(paramsMap) < 1 {
		return sSQL
	}

	sSQL = StrReplaceBraceByMap(sSQL, paramsMap)
	// 以前用于兼容老版本#^#变量k#^#格式，新格式改为：{{变量k}}; 完全放弃老格式后，当前函数可以改为StrReplaceBraceByMap

	sFlag := "#^#"
	if strings.Index(sSQL, sFlag) < 0 {
		return sSQL
	}
	//g.Log().Debug(ctxBG, "FormatSQLMap", paramsMap)

	for k, v := range paramsMap {
		//g.Log().Debug(ctxBG, "FormatSQLMap", k, v)
		p := strings.Join([]string{sFlag, k, sFlag}, "")
		sSQL = strings.ReplaceAll(sSQL, p, strings.Join([]string{v}, ""))
	}
	//fmt.Println("Format SQL:", sSQL)
	g.Log().Debug(ctxBG, "Final SQL:", sSQL)
	return sSQL
}

// FormatSQLMapStrInterface 根据Json字符串里的内容，对SQL语句中的变量进行替换。
func FormatSQLMapStrInterface(sSQL string, paramsMap map[string]interface{}) string {
	//g.Log().Debug(ctxBG, "\n", sSQL, paramsMap)
	if len(paramsMap) < 1 {
		return sSQL
	}

	sSQL = StrReplaceBraceByMapStrInterface(sSQL, paramsMap)
	// 以前用于兼容老版本#^#变量k#^#格式，新格式改为：{{变量k}}; 完全放弃老格式后，当前函数可以改为StrReplaceBraceByMap

	sFlag := "#^#"
	if strings.Index(sSQL, sFlag) < 0 { // 不包含老分割符，退出
		return sSQL
	}
	//g.Log().Debug(ctxBG, "FormatSQLMap", paramsMap)

	for k, v := range paramsMap {
		//g.Log().Debug(ctxBG, "FormatSQLMap", k, v)
		p := strings.Join([]string{sFlag, k, sFlag}, "")
		sSQL = strings.ReplaceAll(sSQL, p, strings.Join([]string{String(v)}, ""))
	}
	//fmt.Println("Format SQL:", sSQL)
	g.Log().Debug(ctxBG, "Final SQL:", sSQL)
	return sSQL
}

// RowsToString 把SQL返回值转换为map
func RowsToString(rows *sql.Rows) ([]map[string]string, error) {
	columns, _ := rows.Columns()            //获取列的信息
	count := len(columns)                   //列的数量
	var values = make([]interface{}, count) //创建一个与列的数量相当的空接口
	for i, _ := range values {
		var ii interface{} //为空接口分配内存
		values[i] = &ii    //取得这些内存的指针，因后继的Scan函数只接受指针
	}
	ret := make([]map[string]string, 0) //创建返回值：不定长的map类型切片
	for rows.Next() {
		err := rows.Scan(values...)  //开始读行，Scan函数只接受指针变量
		m := make(map[string]string) //用于存放1列的 [键/值] 对
		if err != nil {
			//g.Log().Debug(ctxBG, "RowsToString", "SQLResultRowsToMap Error")
			g.Log().Debug(ctxBG, "RowsToString", err)
			return nil, err
		}
		for i, colName := range columns {
			var rawValue = *(values[i].(*interface{})) //读出raw数据，类型为byte
			m[colName] = String(rawValue)
		}
		ret = append(ret, m) //将单行所有列的键值对附加在总的返回值上（以行为单位）
	}
	defer rows.Close()
	if len(ret) != 0 {
		return ret, nil
	}
	return nil, nil
}

//func OrSQL(authSql string, roleDefine6 string) string {
//	//根据role6: Define字段的值，通过配置转换为newSql
//	sqlMap, err := Query("select sqlstring from sys_sql where code='" + roleDefine6 + "'")
//	newSql := ""
//	if err != nil {
//		newSql = roleDefine6
//	} else {
//		if len(sqlMap) < 1 {
//			newSql = roleDefine6
//		} else {
//			newSql = sqlMap[0]["sqlstring"]
//		}
//	}
//	if authSql == "" {
//		return newSql
//	} else {
//		return strings.Join([]string{"(", authSql, " or ", newSql, ")"}, "")
//	}
//}

// OrSQLString 用于SQL Where语句 条件拼接，
func OrSQLString(sourceSql string, newSql string) string {
	if sourceSql == "" {
		return newSql
	} else {
		if newSql == "" {
			return sourceSql
		}
		return strings.Join([]string{"(", sourceSql, " or ", newSql, ")"}, "")
	}
}

// Query 通用查询函数
func Query(ctx context.Context, sqlString string, args ...interface{}) ([]map[string]string, error) {
	g.Log().Debug(ctx, "Query通用查询功能：", sqlString)
	if sqlString == "" {
		return nil, errors.New("查询条件为空！")
	}

	if result, err := g.DB().Query(ctx, sqlString, args...); err != nil {
		return nil, err
	} else {
		resultMap := make([]map[string]string, len(result))
		for _, row := range result {
			ret := make(map[string]string)
			for k, v := range row {
				ret[k] = gconv.String(v)
			}
			resultMap = append(resultMap, ret)
		}
		return resultMap, nil
	}
}

//TDBExec 通用执行SQL语句函数（增删改，不包含查）
//func TDBExec(SQL string) (insId int64, err error) {
//	g.Log().Debug(ctxBG, "DBExec", SQL)
//	if initDB() == true {
//		ret, err := DB.Exec(SQL) //增、删、改就靠这一条命令就够了，但是查不用这个
//		if err != nil {
//			return -1, errors.New("执行SQL语句错误：" + SQL)
//		}
//		return ret.LastInsertId()
//	} else {
//		return -1, errors.New("数据库初始化错误")
//	}
//}
/*
// DBQuery 通用查询函数
func DBQuery(SQL string) ([]map[string]string, error) {
	g.Log().Debug(ctxBG, "DBQuery:", SQL)
	if initDB() != true { //连接数据库
		return nil, errors.New("数据库初始化错误")
	}
	rows, err1 := DB.Query(SQL) //执行SQL语句，比如select * from users
	if err1 != nil {
		panic(err1)
	}
	columns, _ := rows.Columns()            //获取列的信息
	count := len(columns)                   //列的数量
	var values = make([]interface{}, count) //创建一个与列的数量相当的空接口
	for i, _ := range values {
		var ii interface{} //为空接口分配内存
		values[i] = &ii    //取得这些内存的指针，因后继的Scan函数只接受指针
	}
	ret := make([]map[string]string, 0) //创建返回值：不定长的map类型切片
	for rows.Next() {
		err := rows.Scan(values...)  //开始读行，Scan函数只接受指针变量
		m := make(map[string]string) //用于存放1列的 [键/值] 对
		if err != nil {
			panic(err)
		}
		for i, colName := range columns {
			var raw_value = *(values[i].(*interface{})) //读出raw数据，类型为byte
			b, _ := raw_value.([]byte)
			v := string(b) //将raw数据转换成字符串
			m[colName] = v //colName是键，v是值
		}
		ret = append(ret, m) //将单行所有列的键值对附加在总的返回值上（以行为单位）
	}
	defer rows.Close()
	if len(ret) != 0 {
		return ret, nil
	}
	return nil, nil
}

// RowsToString 把SQL返回值转换为map
func RowsToString(rows *sql.Rows) ([]map[string]string, error) {
	columns, _ := rows.Columns()            //获取列的信息
	count := len(columns)                   //列的数量
	var values = make([]interface{}, count) //创建一个与列的数量相当的空接口
	for i, _ := range values {
		var ii interface{} //为空接口分配内存
		values[i] = &ii    //取得这些内存的指针，因后继的Scan函数只接受指针
	}
	ret := make([]map[string]string, 0) //创建返回值：不定长的map类型切片
	for rows.Next() {
		err := rows.Scan(values...)  //开始读行，Scan函数只接受指针变量
		m := make(map[string]string) //用于存放1列的 [键/值] 对
		if err != nil {
			g.Log().Debug(ctxBG, "RowsToString", "SQLResultRowsToMap Error")
			return nil, err
		}
		for i, colName := range columns {
			var rawValue = *(values[i].(*interface{})) //读出raw数据，类型为byte
			m[colName] = gconv.String(rawValue)
		}
		ret = append(ret, m) //将单行所有列的键值对附加在总的返回值上（以行为单位）
	}
	defer rows.Close()
	if len(ret) != 0 {
		return ret, nil
	}
	return nil, nil
}



// QueryMap 通用查询函数-只有map返回值，没有err返回值
func QueryMap(sqlString string) []map[string]string {
	g.Log().Debug(ctxBG, "QueryMap通用查询功能：", sqlString)
	if sqlString == "" {
		g.Log().Info(ctxBG, "QueryMap 参数为空")
		return nil
	}
	//db := g.DB()
	if rows, err := DB.Query(sqlString); err != nil {
		defer rows.Close()
		g.Log().Error(ctxBG, "QueryMap DB查询错误：", sqlString, "\n", err)
		return nil
	} else {
		defer rows.Close()
		ret, err := RowsToString(rows)
		if err != nil {
			g.Log().Error(ctxBG, "QueryMap DB查询结果转换错误：", sqlString, "\n", err)
			return nil
		}
		return ret
	}
}

*/
